﻿// parametry startowe dla wezyka
var StartSpeed = 0.075;
var StartThickness = 4.5;
var StartRadius = 40;
var StartAngle = 90;

// klasa reprezentujaca wezyk
function Snake(number, name, color, leftKey, rightKey) {
    // losowanie miejsca gdzie pojawi sie wężyk
    this.getRandomPlaceOnBoard = function (maxSize) {
        var part = 0.5; // ułamek planszy na jakim pojawiają sie wężyki
        return Math.floor((Math.random() * maxSize * part) + ((maxSize - maxSize * part) / 2));
    };
    this.speed = StartSpeed; // predkosc
    this.radius = StartRadius;
    this.z = 2 * this.radius * Math.sin(this.speed / 2); // przemieszczenie węża w jednym cyklu
    // losowanie kierunku, w którym będzie na początku skierowany wężyk
    this.getRandomDxDy = function () {
        var dxSign = Math.random() > 0.5 ? -1 : 1;
        var dySign = Math.random() > 0.5 ? -1 : 1;
        var z2 = Math.pow(this.z, 2);
        var dx = Math.random() * z2;
        var dy = z2 - dx;

        return {
            "dx": Math.sqrt(dx) * dxSign,
            "dy": Math.sqrt(dy) * dySign
        };
    }
    this.moveStraight = function () {
        var z = 2 * this.radius * Math.sin(this.speed / 2);
        //console.log("z = " + z);
        //console.log("this.speed = " + this.speed);
        var dxSign = (this.movement.dx > 0) ? 1 : -1;
        var dySign = (this.movement.dy > 0) ? 1 : -1;
        var actualDxDySum = Math.abs(this.movement.dx) + Math.abs(this.movement.dy);
        var dxRatio = Math.abs(this.movement.dx) / actualDxDySum;
        var dyRatio = Math.abs(this.movement.dy) / actualDxDySum;
        var z2 = Math.pow(z, 2);
        //console.log("z2 = " + z2);
        var dx = Math.sqrt(z2 * dxRatio) * dxSign;
        var dy = Math.sqrt(z2 * dyRatio) * dySign
        this.x += dx;
        this.y += dy;

    }
    this.number = number; // nr gracza
    this.name = name;
    this.color = color;
    this.x = this.getRandomPlaceOnBoard(gameWidth);
    this.y = this.getRandomPlaceOnBoard(gameHeight);
    this.movement = this.getRandomDxDy();
    this.makeHole = false;
    this.flying = roundFlyingTime; // czas jaki wezyk lata (rysuje sie sama glowka i nie moze zginac)
    this.thickness = StartThickness; //grubosc
    this.canvas = (function () {
        var canvas = document.createElement("canvas");
        canvas.width = gameWidth;
        canvas.height = gameHeight;
        return canvas;
    })();
    this.ctx = this.canvas.getContext("2d");
    this.tempCanvas = (function () {
        var canvas = document.createElement("canvas");
        canvas.width = gameWidth;
        canvas.height = gameHeight;
        return canvas;
    })();
    this.tempCtx = this.tempCanvas.getContext("2d");
    this.points = 0; // liczba punktow
    this.roundPoints = 0; // liczba punktów zdobyta w danej rundzie
    this.alive = true; //czy wezyk zyje (w nic nie uderzyl)
    this.dying = false; // czy waz umiera - potrzebne do aktualizacji punktacji
    this.leftKey = leftKey; // skrecanie w lewo
    this.rightKey = rightKey; // skrecanie w prawo
    this.rightKeyPressed = false;
    this.rightKeyPressedFirstTime = true;
    this.leftKeyPressed = false;
    this.leftKeyPressedFirstTime = true;
    this.angle = StartAngle;
    this.myColisionTable = (function () {
        var table = new Array(gameHeight);
        for (var i = 0; i < gameHeight; i++) {
            table[i] = new Array(gameWidth);
        }
        return table;
    })();
    var xOrigin;
    var yOrigin;
    var bonusArray = new Array();
    var bonusFinishing = false; // jezeli skonczy sie runda, to moze w tym czasie konczyc sie dzialanie niektorych bonusow, to zapobiega przeklamaniom
    var wall = 0; // czas pozostaly do przechodzenia przez sciany

    // zresetowanie danych wezyka dla nowej rundy
    this.resetSnake = function resetSnake() {
        this.x = this.getRandomPlaceOnBoard(gameWidth);
        this.y = this.getRandomPlaceOnBoard(gameHeight);
        this.speed = StartSpeed;
        this.radius = StartRadius;
        this.movement = this.getRandomDxDy();
        this.makeHole = false;
        this.thickness = StartThickness;
        this.roundPoints = 0;
        this.alive = true;
        this.dying = false;
        this.angle = StartAngle;
        this.ctx.clearRect(0, 0, gameWidth, gameHeight);
        this.tempCtx.clearRect(0, 0, gameWidth, gameHeight);
        this.fly(roundFlyingTime);
        this.bonusArray = new Array();
        this.bonusFinishing = false;
        this.wall = 0;

        this.cleanColisionTable();
    }

    // obliczenie oraz ustawienie nowych wspolrzednych ( x i y ) ruchu wezyka
    this.move = function () {
        if (!this.makeHole && !this.flying) {
            this.makeHole = (Math.random() > 0.99) ? true : false;
            if (this.makeHole) {
                var snake = this;
                var time = (this.thickness*8)/this.z*(1000/fps);
                setTimeout(function () { snake.makeHole = false; }, time);
                //this.tempCtx.drawImage(this.canvas, 0, 0);
            }
            this.tempCtx.drawImage(this.canvas, 0, 0);
        }
        //console.log(this.makeHole);
        var oldX = this.x;
        var oldY = this.y;
        // poruszanie po łuku
        if (this.leftKeyPressed || this.rightKeyPressed) {
            // w prawo
            if (this.rightKeyPressed) {
                if (this.rightKeyPressedFirstTime || this.turningLeft) {
                    this.setTurningParameters('right');
                    this.turningRight = true;
                    this.turningLeft = false;
                }
                this.angle -= this.speed;
            }
            // w lewo
            else if (this.leftKeyPressed) {
                if (this.leftKeyPressedFirstTime || this.turningRight) {
                    this.setTurningParameters('left');
                    this.turningLeft = true;
                    this.turningRight = false;
                }
                this.angle += this.speed;
            }
            this.x = this.xOrigin + Math.sin(this.angle) * this.radius;
            this.y = this.yOrigin + Math.cos(this.angle) * this.radius;
            this.movement.dx = this.x - oldX;
            this.movement.dy = this.y - oldY;
        }
        // poruszanie po prostej
        else {
            this.moveStraight();
        }
        
        if (!this.makeHole && !this.flying) {
            this.checkColisions();
            if (!displayingWinner)
                this.checkCoordinatesInColisionTable();
        }

        checkIfBonusTaken(this);

        // kolejny raz sprawdzamy czy wezyk zyje, poniewaz mogl w cos uderzyc i zginac
        if (this.alive) {
            if (this.makeHole || this.flying) {
                this.ctx.clearRect(0, 0, gameHeight, gameWidth);
                this.ctx.drawImage(this.tempCanvas, 0, 0);
            }
            // rysowanie koleczek

            //this.ctx.beginPath();
            //this.ctx.fillStyle = this.color;
            //this.ctx.arc(this.x, this.y, this.thickness, 0, Math.PI * 2, true);
            //this.ctx.closePath;
            //this.ctx.fill();

            // rysowanie linii
            this.ctx.strokeStyle = this.color;
            this.ctx.lineWidth = this.thickness*2;
            this.ctx.lineCap = 'round';
            this.ctx.beginPath();
            this.ctx.moveTo(this.x - this.movement.dx, this.y - this.movement.dy);
            this.ctx.lineTo(this.x, this.y);
            this.ctx.stroke();
        }
    }

    // obliczenie i ustawienie parametrow potrzebnych do skrecania (srodek kola i kat)
    this.setTurningParameters = function (direction) {
        var circleCenter = getCircleCenter(this.x, this.y, this.movement.dx, this.movement.dy, direction, this.radius);
        this.xOrigin = circleCenter.x;
        this.yOrigin = circleCenter.y;
        this.angle = Math.atan2(this.y - this.yOrigin, this.xOrigin - this.x) - Math.radians(90);
        if (direction === 'right') {
            this.rightKeyPressedFirstTime = false;
        }
        else {
            this.leftKeyPressedFirstTime = false;
        }
    }

    // sprawdzenie czy nie nastapila kolizja. Jezeli tak to wezyk ginie
    this.checkColisions = function () {
        // jezeli wezyk uderzyl w sciane
        if (!this.flying && (this.x - this.thickness / 2 <= 0 || this.x + this.thickness / 2 >= gameWidth ||
            this.y - this.thickness <= 0 || this.y + this.thickness / 2 >= gameHeight)) {
            // jezeli moze przechodzi przez sciany
            if (this.wall > 0) {
                if (this.x <= 0)
                    this.x = gameWidth;
                else if (this.x >= gameWidth)
                    this.x = 0;
                else if (this.y <= 0)
                    this.y = gameHeight;
                else if (this.y >= gameHeight)
                    this.y = 0;
            }
            else {
                this.alive = false;
                this.dying = true;
            }
            return;
        }

        // sprawdzenie czy w danym miejscu znajduje sie juz jakis slad po wezyku
        var coordinates = this.getCoordinatesToCheck();

        for (var i = coordinates.x1; i < coordinates.x2; i++) {
            for (var j = coordinates.y1; j < coordinates.y2; j++) {
                // zderzenie z innym wężykiem
                if (colisionTable[i][j] != undefined && colisionTable[i][j] != this.number) {
                    this.alive = false;
                    this.dying = true;
                    return;
                }
                // zderzenie ze sobą samym
                if (this.myColisionTable[i][j] == this.number) {
                    this.alive = false;
                    this.dying = true;
                    // console.log("i", i, "j", j);
                    // console.log("x", this.x, "y", this.y);
                    // console.log("dx", this.movement.dx, "dy", this.movement.dy);
                    // console.log(this.myColisionTable[i][j]);
                    return;
                }
            }
        }
    }

    // zaznaczenie nowo obliczonych wspolrzednych w tablicy kolizji (wraz z calym obszarem jaki zajmie wezyk - pola kola)
    this.checkCoordinatesInColisionTable = function () {
        var coordinates = this.getCoordinatesToCheck();

        for (var i = coordinates.x1; i < coordinates.x2; i++) {
            for (var j = coordinates.y1; j < coordinates.y2; j++) {
                colisionTable[i][j] = parseInt(this.number);
            }
        }

        var snake = this;
        var number = parseInt(this.number);
        setTimeout(function () {
            for (var i = coordinates.x1; i < coordinates.x2; i++) {
                for (var j = coordinates.y1; j < coordinates.y2; j++) {
                    snake.myColisionTable[i][j] = number;
                }
            }
            //console.log("[Coordinates] x1: ", coordinates.x1, " x2: ", coordinates.x2, " y1: ", coordinates.y1, " y2: ", coordinates.y2);
            //console.log("[Actual] x: ", snake.x, " y", snake.y);
        }, 500);
    }

    // odliczanie czasu do konca latania wezyka
    this.fly = function fly(seconds) {
        var snake = this; // zapamietanie obiektu poniewaz potem this wskazuje na co innego
        this.flying = seconds;
        for (var i = 0; i < seconds; i++)
            setTimeout(function () { if (snake.flying > 0) { snake.flying -= 1; console.log(snake.flying); }}, 1000 * (i + 1));
    }

    // odliczanie czasu do konca przechodzenia przez sciany
    this.goWall = function goWall(seconds) {
        var snake = this; // zapamietanie obiektu poniewaz potem this wskazuje na co innego
        this.wall = seconds;
        $('#boardDiv').css('border', 'dashed 5px white');
        for (var i = 0; i < seconds; i++)
            setTimeout(function () { snake.wall -= 1; }, 1000 * (i + 1));
        setTimeout(function () { $('#boardDiv').css('border', 'solid 5px white'); }, 1000 * seconds);
    }

    // na podstawie x, y oraz grubosci wezyka oblicza pixele ktore zostana przez niego zasloniete
    this.getCoordinatesToCheck = function () {
        var coordinates = new Object();
        var range = this.thickness - 1;
        coordinates.x1 = Math.round(this.x - range <= 0 ? 0 : this.x - range);
        coordinates.x2 = Math.round(this.x + range >= gameWidth ? gameWidth : this.x + range);
        coordinates.y1 = Math.round(this.y - range <= 0 ? 0 : this.y - range);
        coordinates.y2 = Math.round(this.y + range >= gameWidth ? gameWidth : this.y + range);
        return coordinates;
    }

    // wyczyszczenie tablicy kolizji
    this.cleanColisionTable = function () {
        for (var i = 0; i < gameHeight; i++) {
            for (var j = 0; j < gameWidth; j++) {
                this.myColisionTable[i][j] = undefined;
            }
        }
    }
}
